﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;

namespace Dycox.Workflows.Conditions
{
    class ExpressionalCondition : TransitionCondition
    {
        public ExpressionalCondition(string exprText)
        {
            _expressionText = exprText;
        }

        private string _expressionText;

        public string ExpressionText
        {
            get { return _expressionText; }
        }

        static readonly Regex ParameterNameRegex = new Regex(@"@([a-zA-Z_][a-zA-Z\d_]*)?", RegexOptions.Compiled);

        private Expression CreateExpression(ConditionTestArguments args)
        {
            List<object> values = new List<object>();
            var coll = TypeDescriptor.GetProperties(args);
            MatchEvaluator evaluator = (m) =>
                {
                    object container;
                    if (m.Value == "@")
                        container = (object)args.ReflectionObject ?? (object)args.UseState;
                    else
                    {
                        string name = m.Groups[1].Value;
                        PropertyDescriptor pd = coll[name];
                        if (pd != null)
                        {
                            container = pd.GetValue(args);
                        }
                        else
                        {
                            IWorkflowComponent component = args.Component;

                            Workflow wf = WorkflowManager.GetWorkflow(component);
                            WFStep step = args.GetStep();

                            if (step == null)
                            {
                                var th = args.GetThread();
                                if (th != null) step = th.ActiveStep;
                            }
                            
                            if (step != null )
                                step = wf.Control.GetNearestCompletedStep(step, name);

                            if (step == null)
                                throw new WorkflowException("Cannot find stage " + name + " on process.");

                            container = step.GetUserState(args.Stage);
                        }

                    }

                    int index = values.IndexOf(container);
                    if (index < 0)
                    {
                        index = values.Count;
                        values.Add(container);
                    }

                    return "@" + index.ToString();

                };

            string exprText =  ParameterNameRegex.Replace(_expressionText, evaluator);

            return System.Linq.Dynamic.DynamicExpression.Parse(typeof(bool), exprText, values.ToArray());

        }

        //private static object GetExpressionArgument

        protected internal override bool Test(ConditionTestArguments args)
        {
            var expr =  CreateExpression(args);

            return (bool)Expression.Lambda(expr).Compile().DynamicInvoke();
        }
    }
}
